Skip to content

fix: refresh microphone list on CoreAudio device-list changes#178

Open
shumengliu wants to merge 1 commit into
Starmel:masterfrom
shumengliu:fix/refresh-mic-list-on-coreaudio-changes
Open

fix: refresh microphone list on CoreAudio device-list changes#178
shumengliu wants to merge 1 commit into
Starmel:masterfrom
shumengliu:fix/refresh-mic-list-on-coreaudio-changes

Conversation

@shumengliu

@shumengliu shumengliu commented Jul 8, 2026

Copy link
Copy Markdown

Summary

Fixes #179 — the Microphone menu goes stale when a Bluetooth mic (e.g. AirPods Pro) reconnects: the device shows up in System Settings → Sound but never reappears in the app's menu until relaunch, and recording silently falls back to another device. On a lid-closed MacBook that fallback is the built-in mic, which records pure digital silence that whisper hallucinates into text like "Thank you." (likely the same failure mode as #84).

Root cause

MicrophoneService refreshes availableMicrophones only in init and on AVCaptureDeviceWasConnected/Disconnected notifications. Those AVFoundation notifications are not delivered reliably for Bluetooth audio devices re-registering with CoreAudio. I verified the same AVCaptureDevice.DiscoverySession query returns the missing device when run fresh while the app's list is stale — the refresh triggers are the problem, not discovery.

Fix

  • Observe CoreAudio's kAudioHardwarePropertyDevices on the system object via AudioObjectAddPropertyListenerBlock (main queue) and refresh the list + current mic when it fires. This is the same signal System Settings reacts to, and it fires for every HAL device change regardless of transport. The status bar menu and the in-app picker already rebuild reactively from $availableMicrophones, so no UI changes are needed.
  • Keep selectedMicrophone when the device list is momentarily empty (e.g. mid Bluetooth handover), so the user's saved choice is restored automatically when the device returns instead of being dropped until relaunch.
  • Publish availableMicrophones only when the list actually changed — kAudioHardwarePropertyDevices can fire several times per connect, and every publish rebuilds the status bar menu.

The existing AVFoundation notification observers are left in place; the CoreAudio listener is additive and the change-detection makes duplicate refreshes free.

Test plan

  • Reproduced the bug on macOS 15.5 with AirPods Pro 2 against release 0.1.0: AirPods listed in System Settings but absent from the app menu; app fell back to the lid-closed built-in mic and produced all-zero recordings (verified by sample inspection of the saved WAVs).
  • Verified via a standalone Swift script that a fresh DiscoverySession query sees the reconnected device while the app's list is stale, and that kAudioHardwarePropertyDevices is the reliable change signal.
  • swiftc -parse clean.
  • CI build (this machine has no full Xcode, so this PR relies on the Build Check workflow; happy to iterate if anything fails).

Manual verification steps for anyone with the setup:

  1. Select a Bluetooth mic in the menu bar, confirm dictation works.
  2. Take the AirPods out of range (or let them auto-switch to a phone), then reconnect them to the Mac.
  3. Without relaunching, open the Microphone menu — the AirPods should be listed and re-selected, and recording should capture from them.

🤖 Generated with Claude Code

AVCaptureDeviceWasConnected/Disconnected notifications are not delivered
reliably for Bluetooth devices, so a mic that reconnected (e.g. AirPods
auto-switching back from an iPhone) stayed missing from the microphone
menu until app relaunch, and recording silently fell back to another
device — on a lid-closed MacBook the built-in mic records pure silence,
which whisper hallucinates into text like "Thank you.".

Observe kAudioHardwarePropertyDevices on the system object instead; it
fires for every HAL device change regardless of transport. Also keep the
saved selection across a transient empty device list so it is restored
when the device returns, and publish the list only when it actually
changed so no-op HAL events don't rebuild the status bar menu.

Fixes Starmel#176

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Microphone menu goes stale: Bluetooth mic missing after reconnect, app silently records from wrong (dead) device

2 participants